iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0
SideProject30

初探 Godot系列 第 21

[DAY 21] 組合背景及障礙物 (Dictionary)

  • 分享至 

  • xImage
  •  

今日目標:組合各個場景!


▍前言:

又再經歷了 10 天的遊戲專案設置,回顧這 10 天我們先設定了遊戲顯示大小、建構了背景、角色、移動邏輯及顯示優化、障礙以及狀態特效,是時候開始收斂整個專案將前述內容做統整及合併了!

那~就開始吧!


▍事前準備

今天會使用到這 10 天完成的各個場景。
因為角色和背景都是白色的所以這裡先更新一下背景,區別一下。


▍出發

  • 現在我們讓我們的障礙物和背景結合。

    1. 開啟背景場景並編輯之前建立在 ParallaxBackground 上的腳本。
    2. 宣告我們障礙物的陣列並在右邊屬性面板設定大小為 3,將這幾天建立的三種不會動的障礙物場景拖曳到陣列中。
    @export var obstacles:Array
    

    https://ithelp.ithome.com.tw/upload/images/20231006/20162875QArY9NgqbZ.png
    3. 隨機在背景中加入障礙物。

    宣告場景寬度並在初始化時取得,和之前取得高度的方法一樣。

    var viewpoint_width:float
    func _ready():
        viewpoint_width = get_viewport().size.x
    

    建立一個字典,鍵為背景,值為對應的障礙物陣列,之後我們可以透過背景去取得在那個節點下的障礙物們。

    var backgrounds_to_obstacles:Dictionary
    
    func _ready():
        backgrounds_to_obstacles = {
            background1: [],
            background2: []
        }
    

    這裡我們在背景生成三種障礙物各一個並存到陣列中重複使用。

        # func _ready 裡
        # 從字典中取出所有的鍵,依序遞迴執行下述邏輯。
        for bg in backgrounds_to_obstacles.keys():
            # 在屬性面板設置的障礙物中依序遞迴執行下述邏輯。
            for obstacle in obstacles:
                # 生成障礙物
                var obstacle_instantiate = obstacle.instantiate()
                # 這裡因為不想要遊戲才剛開始就直接出現障礙物所以做了 work around:
                # 生成在往上兩倍高的位置即代表不會被使用者看到的地方。
                obstacle_instantiate.position = Vector2(0, -viewpoint_height*2)
                # 加到 layer 節點下
                bg.add_child(obstacle_instantiate)
                # 加到字典中對應陣列的障礙物陣列中
                backgrounds_to_obstacles[bg].append(obstacle_instantiate)
    

    當遊戲執行時會產生下面的結構(括號及數字僅代表是不同實例)

    |--ParallaxBackground
    |   |--DynamicBG2
    |   |   |--Sprite2D
    |   |   |--INVINCIBLE (2)
    |   |   |--SLOW (2)
    |   |   |--STOP (2)
    |   |
    |   |--DynamicBG1
    |   |   |--Sprite2D
    |   |   |--INVINCIBLE (1)
    |   |   |--SLOW (1)
    |   |   |--STOP (1)
    

    而字典會如下(括號及數字僅代表是不同實例)

    {
    DynamicBG1 : [INVINCIBLE (1), SLOW (1), STOP (1)]
    DynamicBG2 : [INVINCIBLE (2), SLOW (2), STOP (2)]
    }
    

    建立一個隨機取位置的方法,這裡我自己使用 add_childParallaxLayer 下座標 (0, 0) 會在畫面正中央,因此隨機在上下左右各一半的範圍隨機取值。

    func get_randf_pos():
        return Vector2(randf_range(-viewpoint_width/2, viewpoint_width/2), randf_range(-viewpoint_height/2, viewpoint_height/2))
    

    最後更新我們之前建立的滾動背景邏輯。

    func scroll_background(layer:ParallaxLayer):
    # ...
        # 當背景超過螢幕範圍時,
        if offset.y >= viewpoint_height:
        # ... 
            # 透過背景取得背景下的障礙物。
            for obstacle in backgrounds_to_obstacles[layer]:
                # 更新障礙物位置。
                obstacle.position = get_randf_pos()
    

▍執行

Yes

▍完成

完整檔案

extends ParallaxBackground

@export var randomBackground:Array
@export var speed: float = 500

# obstacles
@export var obstacles:Array

var backgrounds_to_obstacles:Dictionary

var background1:ParallaxLayer
var background2:ParallaxLayer

var viewpoint_width:float
var viewpoint_height:float
# Called when the node enters the scene tree for the first time.
func _ready():
	viewpoint_width = get_viewport().size.x
	viewpoint_height = get_viewport().size.y
	
	background1 = $DynamicBG1
	background2 = $DynamicBG2
	
	backgrounds_to_obstacles = {
		background1: [],
		background2: []
	}
	
	# create reused obstacles
	for bg in backgrounds_to_obstacles.keys():
		for obstacle in obstacles:
			var obstacle_instantiate = obstacle.instantiate()
			obstacle_instantiate.position = Vector2(0, -viewpoint_height*2)
			bg.add_child(obstacle_instantiate)
			backgrounds_to_obstacles[bg].append(obstacle_instantiate)

	background2.set_motion_offset(Vector2(0, -viewpoint_height))

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	scroll_background(background1)
	scroll_background(background2)
	
func scroll_background(layer:ParallaxLayer):
	var offset = layer.get_motion_offset()
	
	offset.y += speed * get_process_delta_time()
	
	if offset.y >= viewpoint_height:
		layer.get_node("Sprite2D")\
		.set_texture(randomBackground[randi()%randomBackground.size()])
		offset.y = offset.y - viewpoint_height*2
		
		# set new randf pos for obstacles
		for obstacle in backgrounds_to_obstacles[layer]:
			obstacle.position = get_randf_pos()
		
	layer.set_motion_offset(offset)
		
func get_randf_pos():
	return Vector2(randf_range(-viewpoint_width/2, viewpoint_width/2), randf_range(-viewpoint_height/2, viewpoint_height/2))

:)


上一篇
[DAY 20] 特效建置 (shader, set_shader_parameter)
下一篇
[DAY 22] 組合角色
系列文
初探 Godot30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言